﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Lean.Touch;
using Newtonsoft.Json;
using UniRx;
using UnityEngine;

namespace SKS
{
    /// <summary>
    /// 令牌识别
    /// </summary>
    public class TangibleProvider : MonoBehaviour
    {
        Subject<LeanFinger> subject = new Subject<LeanFinger>();
        //已识别到的令牌列表
        private ReactiveCollection<Token> tangibles = new ReactiveCollection<Token>();
        //json 特征数据
        public List<TokenData> tokens = new List<TokenData>();
        private TouchEngine touchEngine;

        private void Start()
        {
            Application.targetFrameRate = 60;
            touchEngine = TouchEngine.Instance;
            //读取特征点配置数据
            string path = Application.streamingAssetsPath + "/TokenFeature.json";
            tokens = JsonConvert.DeserializeObject<List<TokenData>>(File.ReadAllText(path));

            //注册点击事件
            LeanTouch.OnFingerDown += HandleFingerDown;
            LeanTouch.OnFingerUp += HandleFingerUp;

            //令牌增加移除事件
            tangibles.ObserveAdd().Subscribe(x =>
            {
                Debug.Log("添加一个令牌到数组:" + x.Value.Class);
                x.Value.isValid = true;
                touchEngine.LaunchTokenPlacedOnScreen(new ApplicationTokenEventArgs(x.Value));
            }).AddTo(this);

            tangibles.ObserveRemove().Subscribe(x =>
            {
                Debug.Log("移除一个令牌:" + x.Value.Class);
                //x.Value.isValid = false;
                touchEngine.LaunchTokenRemovedFromScreen(new ApplicationTokenEventArgs(x.Value));
            }).AddTo(this);

            //识别令牌被放置 当3个点被瞬间点击时  识别特征
            subject.Buffer(subject.Throttle(TimeSpan.FromMilliseconds(300))).Where(xs => xs.Count == 3).Subscribe(xs =>
            {
                if (xs[0].Up || xs[1].Up || xs[2].Up)
                {
                    return;
                }
                //检测特征
                var result = TokenMatch(xs[0].ScreenPosition, xs[1].ScreenPosition, xs[2].ScreenPosition);
                if (result.Item1)
                {
                    Debug.Log("一个令牌已加入队列");
                    // var position = xs.Aggregate(new Vector2(0, 0),
                    //     (total, next) => total + (next.ScreenPosition) / 3);
                    //未加入初始方向判断，xs是按照点击顺序排的，如果需要方向，需要重新按固定方式排，目前的方向计算是xs[0]和xs[1]的向量方向
                    tangibles.Add(new Token(xs, result.Item2));
                }
            });

            //每隔1s 检验一次
            Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(_ =>
            {
                //Debug.Log("校验");
                if (!tangibles.All(t => t.isValid))
                {
                    var tempList = tangibles.Where(t => !t.isValid).ToList();
                    tempList.ForEach(token =>
                    {
                        for (var i = 0; i < LeanTouch.Fingers.Count; i++)
                        {
                            token.Points[token.losePoint] = LeanTouch.Fingers[i];
                            var result = TokenMatch(token.Points[0].ScreenPosition, token.Points[1].ScreenPosition,
                                token.Points[2].ScreenPosition);
                            if (result.Item1)
                            {
                                Debug.LogWarning($"令牌{result.Item2} 丢失点通过Interval找回");
                                token.losePoint = -1;
                                token.isValid = true;
                                break;
                            }
                        }
                    });
                }
            }).AddTo(this);
        }
        
        private void HandleFingerDown(LeanFinger obj)
        {
            //丢失的特征点找回 
            foreach (var token in tangibles)
            {
                if (!token.isValid)
                {
                    token.Points[token.losePoint] = obj;

                    var result = TokenMatch(token.Points[0].ScreenPosition, token.Points[1].ScreenPosition,
                        token.Points[2].ScreenPosition);
                    if (result.Item1)
                    {
                        Debug.Log($"令牌{result.Item2} 丢失点找回");
                        token.losePoint = -1;
                        token.isValid = true;
                        return;
                    }
                }
            }
            subject.OnNext(obj);
        }
        
        readonly object FingerUpLock = new object();

        private void HandleFingerUp(LeanFinger obj)
        {
            lock (FingerUpLock)
            {
                foreach (var token in tangibles)
                {
                    if (token.Points.Contains(obj))
                    {
                        //如果是丢失点，继续往下遍历
                        if (token.Points.IndexOf(obj)==token.losePoint)
                        {
                            Debug.Log("这是丢失了的点");
                            continue;
                        }
                        if (token.isValid)
                        {
                            token.isValid = false;
                            if (token.losePoint == -1)
                            {
                                var a = token.Points.IndexOf(obj);
                                token.losePoint = a;
                                Debug.Log($"点{a}丢失");
                            }
                            else
                            {
                                Debug.Log("令牌取消");
                                tangibles.Remove(token);
                                return;
                            }
                        }
                        else
                        {
                            Debug.Log("令牌取消");
                            tangibles.Remove(token);
                            return;
                        }
                    }
                }
            }
        }

        private void Update()
        {
            if (tangibles.Count > 0)
            {
                //Debug.Log("更新令牌位置");
                foreach (var tangible in tangibles)
                {
                    if (tangible.isValid)
                    {
                        touchEngine.LaunchScreenTokenUpdated(new ApplicationTokenEventArgs(tangible));
                    }
                }
            }
        }

        public void OnDisable()
        {
            LeanTouch.OnFingerDown -= HandleFingerDown;
            LeanTouch.OnFingerUp -= HandleFingerUp;
        }

        //特征识别
        Tuple<bool, int> TokenMatch(Vector2 pos1, Vector2 pos2, Vector2 pos3)
        {
            float temp1 = Vector2.Distance(pos1, pos2);
            float temp2 = Vector2.Distance(pos1, pos3);
            float temp3 = Vector2.Distance(pos2, pos3);
            List<float> deltaList = new List<float>(){temp1,temp2,temp3};
            deltaList.Sort();
            
            for (int i = 0; i < tokens.Count; i++)
            {
                tokens[i].lenght.Sort();
                // deltaList.ForEach(d=>Debug.Log("deltaList:"+d));
                // tokens[i].lenght.ForEach(t=>Debug.Log("tokens:"+t));
                // var q = tokens[i].lenght.Join(deltaList, token => token, delta => delta,
                //     (token, delta) =>
                //     {
                //         Debug.Log("token:"+token+" delta:"+delta);
                //         return (delta < token + 10 && delta > token - 10);
                //     } );
                //
                // foreach (var b in q)
                // {
                //     Debug.Log("Join:"+b);
                // }
                // var isMatch =  tokens[i].lenght.Join(deltaList, token => token, delta => delta,
                //     (token, delta) => delta < token + 10 && delta > token - 10).All(p=>p);
                // if (isMatch)
                // {
                //     Debug.Log("匹配成功:" + tokens[i].ID);
                //     return new Tuple<bool, int>(true, tokens[i].ID);
                // }
                
                ///三边比对
                List<bool> tempBool =new List<bool>();
                for (var j = 0; j < tokens[i].lenght.Count; j++)
                {
                    if (deltaList[j] < tokens[i].lenght[j] + 10 && deltaList[j] > tokens[i].lenght[j] - 10)
                    {
                        tempBool.Add(true);
                    }
                    if (tempBool.Count==3)
                    {
                        return new Tuple<bool, int>(true, tokens[i].ID);
                    }
                }
            }
            return new Tuple<bool, int>(false, 0);
        }
    }
}

[Serializable]
public class TokenData
{
    public int ID = 0;
    public List<float> lenght = new List<float>();
}